﻿using Core.ECS.Components;
using Core.Log;
using Core.Rendering.Resources;
using Core.Tools;
using DefaultEcs;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Desktop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace Core.ECS
{
    [DataContract]
    public class Actor
    {
        //public List<IComponent> ComponentsList;
        public Dictionary<Type,IComponent> Components;
        public List<CBehavior> Behaviors;
        [DataMember] public List<string> BehaviorsName;
        [DataMember] private CTransform _transform;
        [DataMember] private CName _name;
        [DataMember] private CSelfActive _active;
        [DataMember] private CModelRenderer _renderer;
        [DataMember] public Guid Id;
        public CTransform Transform => _transform;
        public string Name => _name.Name;
        public bool Active
        {
            get => _active.IsSelfActive;
            set => _active.IsSelfActive = value;
        }
        public CModelRenderer Renderer => _renderer;
        public Actor()
        {
            Id = Guid.NewGuid();
            Components = new();
            Behaviors = new List<CBehavior>();
            BehaviorsName = new List<string>();
            _transform = new CTransform(Id);
            _name = new CName(Id, "Actor");
            _active = new CSelfActive(Id);
            _renderer = null;
            //_renderer = new CModelRenderer(Id);
        }
        public void SetName(string name)
        {
            _name.Name = name;
        }
        public static Actor? GetActor(string actorName)
        {
            if (Scene.Instance.ActorsByName.ContainsKey(actorName))
            {
                return Scene.Instance.ActorsByName[actorName];
            }
            return null;
        }
        public static Actor CreateActor(Actor prefab,Vector3 position,Quaternion quaternion)
        {
            Type type = prefab.GetType();
            PropertyInfo[] properties = type.GetProperties();
            Actor newInstance = (Actor)Activator.CreateInstance(type);
            foreach (PropertyInfo property in properties)
            {
                if (property.CanRead && property.CanWrite)
                {
                    object value = property.GetValue(prefab);
                    property.SetValue(newInstance, value);
                }
            }
            newInstance.Id = Guid.NewGuid();
            Scene.Instance.Actors.Add(newInstance.Id, newInstance);
            int n = 1;
            while (true)
            {
                if (Scene.Instance.ActorsByName.ContainsKey($"{prefab.Name} ({n})"))
                {
                    n++;
                }
                else
                {
                    newInstance.SetName($"{prefab.Name} ({n})");
                    Scene.Instance.ActorsByName.Add(newInstance.Name, newInstance);
                    break;
                }
            }
            return newInstance;
        }
        public static Actor CreateActorWithoutRendererStart()
        {
            Actor actor = new Actor();
            if (Scene.Instance.ActorsByName.ContainsKey("Actor"))
            {
                int n = 1;
                while (true)
                {
                    if (Scene.Instance.ActorsByName.ContainsKey($"Actor ({n})"))
                    {
                        n++;
                    }
                    else
                    {
                        actor.SetName($"Actor ({n})");
                        Scene.Instance.ActorsList.Add(actor);
                        Scene.Instance.ActorsByName.Add(actor.Name, actor);
                        actor._renderer = new CModelRenderer(actor.Id);
                        Scene.Instance.Actors.Add(actor.Id, actor);
                        break;
                    }
                }
            }
            else
            {
                actor.SetName($"Actor");
                Scene.Instance.ActorsList.Add(actor);
                Scene.Instance.ActorsByName.Add(actor.Name, actor);
                actor._renderer = new CModelRenderer(actor.Id);
                Scene.Instance.Actors.Add(actor.Id, actor);
            }
            return actor;
        }
        public static Actor CreateActor()
        {
            Actor actor = new Actor();
            if (Scene.Instance.ActorsByName.ContainsKey("Actor"))
            {
                int n = 1;
                while (true)
                {
                    if (Scene.Instance.ActorsByName.ContainsKey($"Actor ({n})"))
                    {
                        n++;
                    }
                    else
                    {
                        actor.SetName($"Actor ({n})");
                        Scene.Instance.ActorsList.Add(actor);
                        Scene.Instance.ActorsByName.Add(actor.Name, actor);
                        actor._renderer = new CModelRenderer(actor.Id);
                        actor._renderer.Start();
                        Scene.Instance.Actors.Add(actor.Id, actor);
                        break;
                    }
                }
            }
            else
            {
                actor.SetName($"Actor");
                Scene.Instance.ActorsList.Add(actor);
                Scene.Instance.ActorsByName.Add(actor.Name, actor);
                actor._renderer = new CModelRenderer(actor.Id);
                actor._renderer.Start();
                Scene.Instance.Actors.Add(actor.Id, actor);
            }
            return actor;
        }
        public T? GetComponent<T>()
        {
            if (Components.ContainsKey(typeof(T)))
            {
                return (T)Components[typeof(T)];
            }
            return default(T);
        }
        public bool TryGetComponent<T>(out T t)
        {
            if (Components.ContainsKey(typeof(T)))
            {
                t = (T)Components[typeof(T)];
                if(t != null)
                {
                    return true;
                }
                return false;
            }
            else
            {
                t = default;
                return false;
            }
        }
        private void AddComponents(IComponent component)
        {
            if (component == null) return;
            if (Components.ContainsKey(component.GetType()))
            {
                ExLog.InfoLogCore("Actor " + Name + " have Type" +  component.GetType());
            }
            else
            {
                Components.Add(component.GetType(), component);
                component.Id = Id;
            }
        }
        public T AddComponents<T>()
        {
            if (Components.ContainsKey(typeof(T))) return (T)Components[typeof(T)];
            object? obj = Activator.CreateInstance(typeof(T));
            if (obj != null) 
            {
                if (obj is IComponent)
                {
                    IComponent component = (IComponent)obj;
                    component.Id = Id;
                    Components.Add(typeof(T), (IComponent)obj);
                    return (T)component;
                }
            }
            return default;
        }
        public static Actor? GetActor(Guid id)
        {
            if (Scene.Instance.Actors.ContainsKey(id))
            {
                return Scene.Instance.Actors[id];
            }
            else
            {
                return null;
            }
        }
        public void RendererBind()
        {
            if (_renderer == null) return;
            if(_renderer.Material == null) return;
            _renderer.Material.Bind();
            if (_renderer.Model != null)
            {
                foreach (var mesh in _renderer.Model.Meshes)
                {
                    mesh.Bind();
                    if (mesh.IndexCount > 3)
                    {
                        GL.DrawElements(PrimitiveType.Triangles, mesh.IndexCount, DrawElementsType.UnsignedInt, 0);
                    }
                    else
                    {
                        GL.DrawArrays(PrimitiveType.Triangles, 0, mesh.VertexCount);
                    }
                }
            }
        }
        public void RenderStart()
        {
            if (_renderer == null) _renderer = new(Id);
            _renderer.Start();
        }
        public void ClearRender()
        {
            _renderer = null;
        }
        [OnDeserialized]
        public void OnDeserialized(StreamingContext _)
        {
            Components = [];
            //ComponentsList = [];
            //foreach (var component in ComponentsList)
            //{
            //    AddComponents(component);
            //}
            //ComponentsList.Add(_name);
            //ComponentsList.Add(_active);
            //ComponentsList.Add(_renderer);
            //ComponentsList.Add(_transform);
            AddComponents(_name);
            AddComponents(_active);
            AddComponents(_renderer);
            AddComponents(_transform);
            Behaviors = new List<CBehavior>();
            
        }
        public void BehaviorStart()
        {
            foreach (var behaviorName in BehaviorsName)
            {
                Object? obj = TypeHelper.GetObjectByTypeName(behaviorName);
                if (obj != null && (obj is CBehavior))
                {
                    CBehavior behavior = (CBehavior)obj;
                    if (behavior == null) ExLog.InfoLogCore("Behavior = null");
                    else
                    {
                        behavior.Actor = this;
                        Behaviors.Add(behavior);
                        AddComponents(behavior);
                    }
                }
            }
        }
    }
}
